﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using Microsoft.CSharp;
using Microsoft.VisualBasic;

namespace PasrseCode
{
    class Program
    {
        static void Main(string[] args)
        {          
            var ccu = BuildResourceGraph("Telerik.Sitefinity.Samples", "LocalizationMessages");
            var provider = new CSharpCodeProvider();            
            //var provider = new VBCodeProvider();
            using (var file = File.Open("test.cs", FileMode.Create, FileAccess.Write, FileShare.None))
            using (var writer = new StreamWriter(file, Encoding.Unicode))
            {
                provider.GenerateCodeFromCompileUnit(ccu, writer, new CodeGeneratorOptions()
                {
                    BracingStyle = "C",
                    BlankLinesBetweenMembers = true,
                    IndentString = "   ",
                    VerbatimOrder = true,
                    ElseOnClosing = false                    
                });
            }
            Console.WriteLine("Created succesfully");
        }

        public static CodeCompileUnit BuildResourceGraph(string nameSpace, string type)
        {
            CodeCompileUnit compileUnit = new CodeCompileUnit();

            var codeNamespace = new CodeNamespace(nameSpace);
            
            codeNamespace.Imports.Add(new CodeNamespaceImport("Telerik.Sitefinity.Localization"));
            codeNamespace.Imports.Add(new CodeNamespaceImport("Telerik.Sitefinity.Localization.Data"));
            
            compileUnit.Namespaces.Add(codeNamespace);

            var resClass = BuildResourceType(type, ResFactory.GetReader().Read());
            codeNamespace.Types.Add(resClass);

            return compileUnit;
        }

        public static CodeTypeDeclaration BuildResourceType(string typeName, ResourceClassInfo res)
        {
            CodeTypeDeclaration typeDecl = new CodeTypeDeclaration(typeName);
            BuildSummaryComment(typeDecl, "Localization messages for " + res.Info.Name);
            // base types
            typeDecl.BaseTypes.Add(new CodeTypeReference("Resource"));
            
            // attributes
            var objInfoAttr = BuildObjectInfoAttribute(res.Info.Name);
            typeDecl.CustomAttributes.Add(objInfoAttr);            

            // constructors                                  
            AppendConstructors(typeDecl);

            // append default properties            
            AppendResourceProperty(typeDecl, res.Info.Name + "ClassDescription", res.Info.Description, "Resource class description", GetDateTimeString(DateTime.Now))
                .StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Class description"));
            AppendResourceProperty(typeDecl, res.Info.Name + "TitleSingular", res.Info.Title, "Singular title", GetDateTimeString(DateTime.Now));
            AppendResourceProperty(typeDecl, res.Info.Name + "TitlePlural", res.Info.PluralTitle, "Plural title", GetDateTimeString(DateTime.Now))
                .EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, "Class description"));

            for (int i = 0; i < res.Entries.Count; i++)
            {
                var entry = res.Entries[i];
                var prop = AppendResourceProperty(typeDecl, entry.ClassID, entry.Translation, entry.Description, entry.ModDate);
                if (i == 0)
                    prop.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "User translations"));
                if (i == res.Entries.Count - 1)
                    prop.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, "User translations"));
            }

            return typeDecl;
        }

        private static string GetDateTimeString(DateTime dt)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(dt.Year);
            sb.Append('/');
            sb.Append(dt.Month.ToString("D2"));
            sb.Append('/');
            sb.Append(dt.Day.ToString("D2"));            
            return sb.ToString();
        }

        public static CodeMemberProperty BuildResourceProperty(string classID, string translation, string description, string modDate)
        {
            var prop = new CodeMemberProperty();
            BuildSummaryComment(prop, "Translated message, similar to \"" + translation + "\"");
            BuildValueComment(prop, description);

            var entryAttrib = BuildResourceEntryAttribute(classID, translation, description, modDate);
            prop.CustomAttributes.Add(entryAttrib);

            prop.Attributes = MemberAttributes.Public;
            prop.Name = classID;
            prop.HasGet = true;
            var indexerExpr = new CodeIndexerExpression();
            indexerExpr.TargetObject = new CodeBaseReferenceExpression();
            indexerExpr.Indices.Add(new CodePrimitiveExpression(classID));
            var returnStatement = new CodeMethodReturnStatement(indexerExpr);
            prop.GetStatements.Add(returnStatement);
            return prop;
        }

        public static CodeMemberProperty AppendResourceProperty(CodeTypeDeclaration resClass, string classID, string translation, string description, string modDate)
        {
            var prop = BuildResourceProperty(classID, translation, description, modDate);
            resClass.Members.Add(prop);
            return prop;
        }

        public static void AppendConstructors(CodeTypeDeclaration resClass)
        {
            var emptyCtor = BuildEmptyConstructor();
            emptyCtor.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Constructors"));
            resClass.Members.Add(emptyCtor);
            var providerCtor = BuildProviderConstructor();
            providerCtor.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, "Constructors"));
            resClass.Members.Add(providerCtor);
        }

        public static CodeConstructor BuildEmptyConstructor()
        {
            var emptyCtor = new CodeConstructor();
            emptyCtor.Attributes = MemberAttributes.Public;
            BuildSummaryComment(emptyCtor, "Creates a new instance");
            return emptyCtor;
        }

        public static CodeConstructor BuildProviderConstructor()
        {
            var providerCtor = new CodeConstructor();
            providerCtor.Attributes = MemberAttributes.Public;
            BuildSummaryComment(providerCtor, "Creates a new instance by setting a resources provider");
            BuildParamComment(providerCtor, "provider", "Localization provider");
            providerCtor.Parameters.Add(new CodeParameterDeclarationExpression("ResourceDataProvider", "provider"));
            providerCtor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("provider"));
            return providerCtor;          
        }

        public static void BuildSummaryComment(CodeTypeMember member, string summary)
        {
            member.Comments.Add(new CodeCommentStatement("<summary>", true));
            member.Comments.Add(new CodeCommentStatement(summary, true));
            member.Comments.Add(new CodeCommentStatement("</summary>", true));
        }

        public static void BuildValueComment(CodeTypeMember member, string valueComment)
        {
            member.Comments.Add(new CodeCommentStatement(string.Concat("<value>", valueComment, "</value>"), true));            
        }
        
        public static void BuildParamComment(CodeTypeMember member, string name, string description)
        {
            member.Comments.Add(new CodeCommentStatement(string.Format("<param name=\"{0}\">{1}</param>", name, description), true));          
        }

        public static CodeAttributeDeclaration BuildResourceEntryAttribute(string key, string value, string description, string lastModified)
        {
            CodeAttributeDeclaration entry = new CodeAttributeDeclaration("ResourceEntry");
            var keyArg = new CodeAttributeArgument(new CodePrimitiveExpression(key));
            var valueArg = new CodeAttributeArgument("Value", new CodePrimitiveExpression(value));
            var descriptionArg = new CodeAttributeArgument("Description", new CodePrimitiveExpression(description));
            var lastModifiedArg = new CodeAttributeArgument("LastModified", new CodePrimitiveExpression(lastModified));

            entry.Arguments.AddRange(new CodeAttributeArgument[] { keyArg, valueArg, descriptionArg, lastModifiedArg });

            return entry;
        }

        public static CodeAttributeDeclaration BuildObjectInfoAttribute(string resClassName)
        {            
            var nameArg = new CodeAttributeArgument(new CodePrimitiveExpression(resClassName));
            var descriptionArg = new CodeAttributeArgument("Description", new CodePrimitiveExpression(resClassName + "ClassDescription"));
            var titleArg = new CodeAttributeArgument("Title", new CodePrimitiveExpression(resClassName + "TitleSingular"));
            var titlePluralArg = new CodeAttributeArgument("TitlePlural", new CodePrimitiveExpression(resClassName + "TitlePlural"));
            var resClassIdArg = new CodeAttributeArgument("ResourceClassId", new CodePrimitiveExpression(resClassName));

            var objInfo = new CodeAttributeDeclaration("ObjectInfo", nameArg, descriptionArg, titleArg, titlePluralArg);
            return objInfo;
        }
    }
}
